package com.sc.nft.dao;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.sc.nft.entity.SecondCollection;
import com.sc.nft.entity.dto.SecondConSignCollectionDTO;
import com.sc.nft.enums.ConSignStatus;
import com.sc.nft.enums.MarketTypeEnum;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.ElasticsearchHelper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.sc.nft.sup.ErrorCode.REQUEST_FAILED_PLEASE_TRY_AGAIN_LATER;


@Slf4j
@Repository
@RequiredArgsConstructor
public class SecondCollectionEsDao {

    private final ElasticsearchHelper helper;

    @Value("${app.metago.es_index.sc_second_collection:meta-sc_second_collection-alias}")
    private String indexAlias;

    @Value("${app.metago.admin-view-consign-page:meta-admin-view-consign-page-alias}")
    private String adminViewConsignPageIndexAlias;

    @SneakyThrows
    public void save(SecondCollection doc) {
        try {
            IndexResponse response = helper.index(indexAlias, doc.getId(), doc);
            log.info("写入寄售数据 id: {}, version: {}, result: {}", response.getId(), response.getVersion(), response.getResult());
        } catch (IOException e) {
            log.error("ES写入寄售数据出错 #save(), doc: {}", doc, e);
        }
    }

//    public void update(SecondCollection doc) {
//        try {
//            UpdateResponse response = helper.update(indexAlias, doc.getId(), doc);
//            log.info("更新寄售数据 id: {}, version: {}, result: {}", response.getId(), response.getVersion(), response.getResult());
//        } catch (IOException e) {
//            log.error("ES更新寄售数据出错 #update(), doc: {}", doc, e);
//        }
//    }

    @SneakyThrows
    public SecondCollection findById(long id) {
        return helper.get(indexAlias, String.valueOf(id), SecondCollection.class);
    }

    // -------------------- 后台寄售列表宽表CRUD --------------------

    /**
     * 后台寄售市场>寄售列表宽表数据维护
     */
    public void saveAdminViewConsignPage(SecondConSignCollectionDTO doc) {
        try {
            IndexResponse response = helper.index(adminViewConsignPageIndexAlias, doc.getId(), doc);
            log.info("写入后台寄售宽表数据 id: {}, version: {}, result: {}", response.getId(), response.getVersion(), response.getResult());
        } catch (IOException e) {
            log.error("ES写入后台寄售宽表数据出错 #saveAdminViewConsignPage(), doc: {}", doc, e);
        }
    }

    public void updateAdminViewConsignPage(long id, ConSignStatus status) {
        Map<String, Object> doc = MapUtil.<String, Object>builder()
                .put("status", status)
                .build();
        try {
            UpdateResponse response = helper.update(adminViewConsignPageIndexAlias, id, doc);
            log.info("更新后台寄售宽表数据 id: {}, version: {}, result: {}", response.getId(), response.getVersion(), response.getResult());
        } catch (IOException e) {
            log.error("ES更新后台寄售宽表数据出错 id: {}, doc: {}", id, doc, e);
        } catch (ElasticsearchException e) {
            log.warn("ES更新后台寄售宽表数据失败 id: {}, doc: {}, status: {}", id, doc, e.status(), e);
        }
    }

    private BoolQueryBuilder buildAdminConsignPageQuery(Date startTime, Date endTime, String phone, ConSignStatus status, Integer marketType) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        if (startTime != null && endTime != null) {
            boolQueryBuilder.filter(new RangeQueryBuilder("consignTime").gt(DateUtil.formatDateTime(startTime)).lt(DateUtil.formatDateTime(endTime)));
        } else if (startTime != null) {
            boolQueryBuilder.filter(new RangeQueryBuilder("consignTime").gt(DateUtil.formatDateTime(startTime)));
        } else if (endTime != null) {
            boolQueryBuilder.filter(new RangeQueryBuilder("consignTime").lt(DateUtil.formatDateTime(endTime)));
        }
        if (status != null && status != ConSignStatus.ALL) {
            boolQueryBuilder.filter(new TermQueryBuilder("status", status));
        }
        if (StrUtil.isNotBlank(phone)) {
            boolQueryBuilder.filter(new TermQueryBuilder("phone", phone));
        }
        if (marketType != null) {
            MarketTypeEnum e = MarketTypeEnum.getMarketTypeEnum(marketType);
            if (e != null) {
                boolQueryBuilder.filter(new TermQueryBuilder("marketType", e.name()));
            }
        }

        return boolQueryBuilder;
    }

    public long countAdminConsignPage(Date startTime, Date endTime, String phone, ConSignStatus status, Integer marketType) {
        try {
            BoolQueryBuilder boolQueryBuilder = buildAdminConsignPageQuery(startTime, endTime, phone, status, marketType);
            CountResponse countResponse = helper.count(new CountRequest(adminViewConsignPageIndexAlias).query(boolQueryBuilder));
            return countResponse.getCount();
        } catch (ElasticsearchException e) {
            log.warn("ES搜索后台寄售宽表数据失败 status: {}", e.status(), e);
        } catch (IOException e) {
            log.error("ES搜索后台寄售宽表数据出错", e);
        }

        throw new GlobalRunTimeException(REQUEST_FAILED_PLEASE_TRY_AGAIN_LATER);
    }

    public List<SecondConSignCollectionDTO> getAdminConsignPage(Date startTime, Date endTime, String phone, ConSignStatus status, int pageNo, int pageSize, Integer marketType) {

        try {
            SearchRequest request = new SearchRequest(adminViewConsignPageIndexAlias);

            BoolQueryBuilder boolQueryBuilder = buildAdminConsignPageQuery(startTime, endTime, phone, status, marketType);

            // 页码从1开始计算
            if (pageNo < 1) {
                pageNo = 1;
            }
            request.source(new SearchSourceBuilder()
                    .from((pageNo - 1) * pageSize)
                    .size(pageSize)
                    .query(boolQueryBuilder)
                    .sort("id", SortOrder.DESC)
            );

            return helper.search(request, SecondConSignCollectionDTO.class);
        } catch (ElasticsearchException e) {
            log.warn("ES搜索后台寄售宽表数据失败 status: {}", e.status(), e);
        } catch (IOException e) {
            log.error("ES搜索后台寄售宽表数据出错", e);
        }

        throw new GlobalRunTimeException(REQUEST_FAILED_PLEASE_TRY_AGAIN_LATER);
    }

    @SneakyThrows
    public SecondCollection findById(Long id) {
        return helper.get(indexAlias, id.toString(), SecondCollection.class);
    }

    @SneakyThrows
    public List<SecondCollection> findByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }

        return helper.mget(indexAlias,
                ids.stream().filter(Objects::nonNull).map(Object::toString).collect(Collectors.toList()),
                SecondCollection.class);
    }

    @SneakyThrows
    public int countSelfSalesCollection(Long collectionId, Long userId) {
        CountResponse response = helper.count(new CountRequest(indexAlias).query(new BoolQueryBuilder()
                .filter(new TermQueryBuilder("collectionId", collectionId))
                .filter(new TermQueryBuilder("userId", userId))
                .filter(new TermQueryBuilder("isDelete", false))
        ));
        return (int) response.getCount();
    }

    @SneakyThrows
    public SecondCollection selectByUserCollectionIdAndUserId(Long userCollectionId, Long userId, ConSignStatus status) {
        List<SecondCollection> items = helper.search(new SearchRequest(indexAlias)
                        .source(new SearchSourceBuilder()
                                .query(
                                        new BoolQueryBuilder()
                                                .filter(new TermQueryBuilder("userCollectionId", userCollectionId))
                                                .filter(new TermQueryBuilder("userId", userId))
                                                .filter(new TermsQueryBuilder("isDelete", false))
                                                .mustNot(new TermQueryBuilder("status", status))

                                ).size(1)
                        )
                , SecondCollection.class);
        return CollUtil.getFirst(items);
    }

    /**
     * 批量写入索引
     */
    public void bulkIndex(List<SecondCollection> entities) throws IOException {
        if (CollectionUtils.isEmpty(entities)) {
            return;
        }

        BulkResponse response = helper.bulkIndex(indexAlias, entities, item -> item.getId().toString());
        log.info("Bulk index status: {}, size: {}", response.status(), response.getItems().length);
    }
}
